#define _CRT_SECURE_NO_WARNINGS
#include "schedule.h"

#include <cstring> // for strcmp()
#include <stdlib.h>   // for atoi(), atol()

Schedule::Schedule()
{
	 infile_p = NULL;
	 outfile_p = NULL;
	 current_time = 0;
	 head_flow_queue = NULL;
	 tail_flow_queue = NULL;
	 current_flow = NULL;
	 need_to_load_next_time = false;
	 end_of_input = false;
	 num_of_flows = 0;
	 counter_of_empty_flows = 0;
}

Schedule::~Schedule()
{

}

int Schedule::Check_Arguments(int argc, char *argv[])
{
	if (argc != ARGS_NUM + 1)
	{
		cerr << "Invalid num of arguments for sch.exe\n";
		return 1;
	}
	if (strcmp(argv[1], "RR") && strcmp(argv[1], "DRR")
		&& strcmp(argv[1], "WRR"))
	{
		cerr << "Invalid scheduler type.\n" <<
			"Only \"RR\" or \"WRR\" or \"DRR\" allowed.\n";
		return 1;

	}
	if (atoi(argv[2]) < 0)
	{
		cerr << "Invalid Quantum\n";
		return 1;
	}
	return 0;
}

int Schedule::Initialize_Schedule(char* infile_name, char* outfile_name)
{
	infile_p = fopen(infile_name, "r");
	if (infile_p == NULL)
	{
		cerr << "Failed to open input file.\n";
		return 1;
	}
	outfile_p = fopen(outfile_name, "w");
	if (outfile_p == NULL)
	{
		cerr << "Failed to open output file.\n";
		return 1;
	}
	if (Get_Pkt_From_File(true))
	{
		cerr << "Failed to Initialize Scheduler.\n";
		return 1;
	}
	if (Load_data_to_Schedule())
	{
		cerr << "Failed to Load data from file./n";
		return 1;

	}

	return 0;
}

int Schedule::Load_data_to_Schedule()
{
	if (time_of_arrived <= current_time)
	{
		if (need_to_load_next_time)
		{
			if (Load_Flow())
			{
				cerr << "Error while trying to insert new packet.\n";
				return -1;
			}
			need_to_load_next_time = false;
		}
		

	}
	else if (empty_queues) // need to promote time
	{
		current_time = time_of_arrived;
		if (Load_Flow())
		{
			cerr << "Error while trying to insert new packet.\n";
			return -1;
		}
	}
		
	else
		return 0;

	int return_val = Get_Pkt_From_File(false);
	while (!return_val)
		return_val = Get_Pkt_From_File(false);
	if (return_val == -1)
	{
		cerr << "Error while trying to insert new packet.\n";
		return -1;
	}
	return 0;

}


int Schedule::Get_Pkt_From_File(bool first_time)
{
	
	if (fgets(line_buf, MAX_LINE_BUF, infile_p) == NULL && !feof(infile_p))
	{
		cerr << "Failed to read from file.\n";
		return -1;
	}
	if (feof(infile_p)) // in case of end of line
	{
		end_of_input = true;
		return 2;
	}
	char* token = strtok(line_buf, " \n");

	pkt_id = atol(token);

	for (int i = 0; i < 7; i++)
	{
		token = strtok(NULL, " \n");
		if (token == NULL && i != 6)
		{
			cerr << "Strtok() failed.\n";
			return -1;

		}			
		else
		{
			switch (i)
			{
			case 0:
				time_of_arrived = atol(token);
				break;
			case 1:
				sadd = token;
				break;
			case 2:
				sport = (unsigned short int )atoi(token);
				break;
			case 3:
				dadd = token;
				break;
			case 4:
				dport = (unsigned short int)atoi(token);
				break;
			case 5:
				length = (unsigned short int)atoi(token);
				break;
			case 6:
				if (token != NULL)
					weight = (unsigned short int)atoi(token);
				else
					weight = 1;
			}
		}
	}
	if (first_time)
	{
		current_time = time_of_arrived;
		try{
			head_flow_queue = new Flow(sadd, sport, dadd, dport, weight);
		}
		catch (std::bad_alloc& ba)
		{
			cerr << "bad_alloc caught: " << ba.what() << '\n';
			return -1;
		}
		tail_flow_queue = head_flow_queue;
		num_of_flows++;
		if (head_flow_queue->Add_Pkt_To_Flow(pkt_id, time_of_arrived, length))
		{
			cerr << "Failed to add packet to queue\n";
			return -1;
		}
	}
	else if (current_time < time_of_arrived)
	{
		need_to_load_next_time = true;
		return 3;
	}
	else // not the first time
	{
		if (Load_Flow())
		{
			cerr << "Error while trying to insert new packet.\n";
			return -1;
		}
	}
	need_to_load_next_time = false;
	return 0;
}

Flow* Schedule::Find_Flow()
{
	Flow* temp_flow = head_flow_queue;

	while (temp_flow != NULL)
	{
		if (temp_flow->Check_Flow(sadd, sport, dadd, dport))
			return temp_flow;
		else
			temp_flow = temp_flow->next_flow;
	}
	return temp_flow;
}


int Schedule::Do_Schedule_RR()
{
	current_flow = head_flow_queue;
	while (true)
	{
		int return_time = current_flow->Remove_Pkt_From_Flow(current_time, outfile_p);
		if (return_time == -1)
		{
			cerr << "Failed to remove packet from flow./n";
			return 1;
		}
		if (return_time > 0)
		{
			counter_of_empty_flows = 0;
			current_time = return_time;
			if (!end_of_input)
			{
				if (Load_data_to_Schedule())
				{
					cerr << "Failed to Load data from file./n";
					return 1;
				}
			}
			Go_to_Next_Flow();
		}
		else // return_time == 0 (no packet in current flow)
		{
			counter_of_empty_flows++;
			if (counter_of_empty_flows == num_of_flows)
			{
				empty_queues = true;

				if (end_of_input)
					break;
				else if (Load_data_to_Schedule())
				{
					cerr << "Failed to Load data from file./n";
					return 1;
				}
				empty_queues = false;
				counter_of_empty_flows = 0;
				current_flow = head_flow_queue;

			}
			else
				Go_to_Next_Flow();
		}
		
	}
	return 0;
}

int Schedule::Load_Flow()
{
	Flow* temp_flow = Find_Flow();
	if (temp_flow != NULL)
	{
		if (temp_flow->Add_Pkt_To_Flow(pkt_id, time_of_arrived, length))
		{
			cerr << "Failed to add packet to queue\n";
			return -1;
		}
	}
	else // need to create new flow
	{
		try{
			temp_flow = new Flow(sadd, sport, dadd, dport, weight);
		}
		catch (std::bad_alloc& ba)
		{
			cerr << "bad_alloc caught: " << ba.what() << '\n';
			return -1;
		}
		tail_flow_queue->next_flow = temp_flow;
		tail_flow_queue = temp_flow;
		num_of_flows++;
		if (temp_flow->Add_Pkt_To_Flow(pkt_id, time_of_arrived, length))
		{
			cerr << "Failed to add packet to queue\n";
			return -1;
		}
	}
	return 0;

}

void Schedule::Go_to_Next_Flow()
{
	if (current_flow == tail_flow_queue)
		current_flow = head_flow_queue;
	else
		current_flow = current_flow->next_flow;
}